"""
事务原子性测试
==============

测试用户创建过程中的事务原子性，确保数据一致性
"""

import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from sqlalchemy.exc import IntegrityError

import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))

from app.services.user_service import UserService
from app.services.transaction_service import TransactionService
from app.repositories.user_repository import UserRepository
from app.repositories.user_profile_repository import UserProfileRepository
from redfire_framework.core.exceptions import DatabaseTransactionError


class TestTransactionAtomicity:
    """事务原子性测试类"""
    
    @pytest.fixture
    def mock_user_repository(self):
        """模拟用户仓储"""
        repo = AsyncMock(spec=UserRepository)
        repo.exists_by_username.return_value = False
        repo.exists_by_email.return_value = False
        repo.get_by_id.return_value = MagicMock(id="user-123", username="testuser")
        return repo
    
    @pytest.fixture
    def mock_profile_repository(self):
        """模拟用户资料仓储"""
        repo = AsyncMock(spec=UserProfileRepository)
        return repo
    
    @pytest.fixture
    def mock_transaction_service(self):
        """模拟事务服务"""
        return AsyncMock(spec=TransactionService)
    
    @pytest.fixture
    def user_service(self, mock_user_repository, mock_profile_repository, mock_transaction_service):
        """用户服务实例"""
        service = UserService(
            user_repository=mock_user_repository,
            user_profile_repository=mock_profile_repository,
            transaction_service=mock_transaction_service,
            jwt_manager=AsyncMock(),
            password_manager=AsyncMock(),
            audit_service=AsyncMock()
        )
        service.password_manager.hash_password.return_value = "hashed_password"
        return service
    
    @pytest.mark.asyncio
    async def test_successful_user_creation_with_transaction(
        self, 
        user_service, 
        mock_user_repository, 
        mock_profile_repository,
        mock_transaction_service
    ):
        """测试成功的用户创建（事务提交）"""
        # 模拟成功的事务上下文
        mock_session = AsyncMock()
        mock_transaction_service.transaction.return_value.__aenter__.return_value = mock_session
        
        # 模拟成功创建用户和资料
        mock_user = MagicMock(id="user-123")
        mock_user_repository.create_with_session.return_value = mock_user
        mock_profile_repository.create_with_session.return_value = MagicMock()
        
        # 执行用户创建
        result = await user_service.create_user(
            username="testuser",
            email="test@example.com",
            password="password123",
            full_name="Test User"
        )
        
        # 验证事务被正确使用
        mock_transaction_service.transaction.assert_called_once()
        
        # 验证用户和资料都在同一事务中创建
        mock_user_repository.create_with_session.assert_called_once_with(
            mock_session, 
            {
                "username": "testuser",
                "email": "test@example.com", 
                "hashed_password": "hashed_password",
                "is_active": True
            }
        )
        
        mock_profile_repository.create_with_session.assert_called_once_with(
            mock_session,
            {
                "user_id": "user-123",
                "full_name": "Test User",
                "phone": None,
                "bio": None
            }
        )
        
        # 验证返回结果
        assert result is not None
    
    @pytest.mark.asyncio
    async def test_user_creation_rollback_on_profile_failure(
        self,
        user_service,
        mock_user_repository,
        mock_profile_repository,
        mock_transaction_service
    ):
        """测试用户资料创建失败时的事务回滚"""
        # 模拟事务上下文
        mock_session = AsyncMock()
        mock_transaction_service.transaction.return_value.__aenter__.return_value = mock_session
        
        # 模拟用户创建成功，但资料创建失败
        mock_user = MagicMock(id="user-123")
        mock_user_repository.create_with_session.return_value = mock_user
        mock_profile_repository.create_with_session.side_effect = IntegrityError(
            "Profile creation failed", None, None
        )
        
        # 模拟事务上下文管理器抛出异常
        mock_transaction_service.transaction.return_value.__aenter__.side_effect = DatabaseTransactionError(
            "Transaction failed and was rolled back: Profile creation failed"
        )
        
        # 执行用户创建，应该失败
        with pytest.raises(DatabaseTransactionError) as exc_info:
            await user_service.create_user(
                username="testuser",
                email="test@example.com",
                password="password123",
                full_name="Test User"
            )
        
        # 验证异常信息
        assert "Transaction failed and was rolled back" in str(exc_info.value)
        
        # 验证事务被调用
        mock_transaction_service.transaction.assert_called_once()
    
    @pytest.mark.asyncio
    async def test_user_creation_rollback_on_user_failure(
        self,
        user_service,
        mock_user_repository,
        mock_profile_repository,
        mock_transaction_service
    ):
        """测试用户创建失败时的事务回滚"""
        # 模拟事务上下文
        mock_session = AsyncMock()
        mock_transaction_service.transaction.return_value.__aenter__.return_value = mock_session
        
        # 模拟用户创建失败
        mock_user_repository.create_with_session.side_effect = IntegrityError(
            "User creation failed", None, None
        )
        
        # 模拟事务上下文管理器抛出异常
        mock_transaction_service.transaction.return_value.__aenter__.side_effect = DatabaseTransactionError(
            "Transaction failed and was rolled back: User creation failed"
        )
        
        # 执行用户创建，应该失败
        with pytest.raises(DatabaseTransactionError) as exc_info:
            await user_service.create_user(
                username="testuser",
                email="test@example.com",
                password="password123"
            )
        
        # 验证异常信息
        assert "Transaction failed and was rolled back" in str(exc_info.value)
        
        # 验证用户资料创建不会被调用（因为用户创建就失败了）
        mock_profile_repository.create_with_session.assert_not_called()
    
    @pytest.mark.asyncio
    async def test_audit_log_failure_does_not_affect_user_creation(
        self,
        user_service,
        mock_user_repository,
        mock_profile_repository,
        mock_transaction_service
    ):
        """测试审计日志失败不影响用户创建"""
        # 模拟成功的事务
        mock_session = AsyncMock()
        mock_transaction_service.transaction.return_value.__aenter__.return_value = mock_session
        
        mock_user = MagicMock(id="user-123")
        mock_user_repository.create_with_session.return_value = mock_user
        mock_profile_repository.create_with_session.return_value = MagicMock()
        
        # 模拟审计服务失败
        user_service.audit_service.log_user_action.side_effect = Exception("Audit failed")
        
        # 执行用户创建，应该成功（尽管审计失败）
        result = await user_service.create_user(
            username="testuser",
            email="test@example.com",
            password="password123"
        )
        
        # 验证用户创建成功
        assert result is not None
        
        # 验证事务正常执行
        mock_transaction_service.transaction.assert_called_once()
        mock_user_repository.create_with_session.assert_called_once()
        mock_profile_repository.create_with_session.assert_called_once()


class TestTransactionServiceIntegration:
    """事务服务集成测试"""
    
    @pytest.mark.asyncio
    async def test_transaction_context_manager_success(self):
        """测试事务上下文管理器成功场景"""
        # 模拟会话工厂
        mock_session = AsyncMock()
        session_factory = AsyncMock(return_value=mock_session)
        
        transaction_service = TransactionService(session_factory)
        
        # 使用事务上下文管理器
        async with transaction_service.transaction() as session:
            assert session == mock_session
            # 模拟一些数据库操作
            pass
        
        # 验证事务流程
        mock_session.begin.assert_called_once()
        mock_session.commit.assert_called_once()
        mock_session.close.assert_called_once()
        mock_session.rollback.assert_not_called()
    
    @pytest.mark.asyncio
    async def test_transaction_context_manager_rollback(self):
        """测试事务上下文管理器回滚场景"""
        # 模拟会话工厂
        mock_session = AsyncMock()
        session_factory = AsyncMock(return_value=mock_session)
        
        transaction_service = TransactionService(session_factory)
        
        # 使用事务上下文管理器，模拟异常
        with pytest.raises(DatabaseTransactionError):
            async with transaction_service.transaction() as session:
                assert session == mock_session
                # 模拟数据库操作失败
                raise Exception("Database operation failed")
        
        # 验证回滚流程
        mock_session.begin.assert_called_once()
        mock_session.rollback.assert_called_once()
        mock_session.close.assert_called_once()
        mock_session.commit.assert_not_called()
